package com.rongji.dfish.demo.editor.service.uiadaptor;

import java.util.ArrayList;
import java.util.List;

public class JsonHighlighter {
	public static String highlight(String json){
		if (json == null) {
			return "";
		}
	 	StringBuilder sb=new StringBuilder();
	 	sb.append("<div id='codePreview'>");
    	char[] chars=json.toCharArray();
    	//双引号里面的逗号要忽略
    	List<JsonBlock> blocks=new ArrayList<JsonBlock>();
    	visitJson(blocks,null,chars,0,0);
    	buildJson(sb,chars,blocks,0,false);
    	sb.append("</div>");
    	return sb.toString();
	}

	private static final int JSON_LENGTH_PER_LINE=80;
	private static int buildJson(StringBuilder sb, char[] chars, List<JsonBlock> blocks,int begin,boolean br) {
		int lineLength=0;
		for(int i=begin;i<blocks.size();i++){
			JsonBlock jb=blocks.get(i);
			switch(jb.type){
				case JsonBlock.TYPE_BLOCK_START:{
					if(br){
						if(i>0&&(blocks.get(i-1).type!=JsonBlock.TYPE_BLOCK_COLON)){
							newLine(sb,jb.level);//FIMXE 可以缩进了//好像是下一个级别进行缩进就可以额了。
							lineLength=0;
						}
					}
					sb.append(chars,jb.begin,1);
					boolean subBr=jb.bracketEnd-jb.begin>JSON_LENGTH_PER_LINE||jb.son;
					i=buildJson(sb,chars,blocks,i+1,subBr);
					break;
				}
				case JsonBlock.TYPE_BLOCK_END:{
					if(br){
						newLine(sb,jb.level-1);//可以缩进了
						lineLength=0;
					}
					sb.append(chars,jb.begin,1);
					return i;
				}
				case JsonBlock.TYPE_BLOCK_COLON:
				case JsonBlock.TYPE_BLOCK_COMMA:{
					sb.append(chars,jb.begin,1);
					lineLength++;
					break;
				}
				case JsonBlock.TYPE_BLOCK_CONTENT:
				case JsonBlock.TYPE_BLOCK_QUTA:{
					//如果下一个节点是 : 则当前这个节点为属性
					//如果前面那个节点为 , 则考虑要不要增加换行
					if(br){
						if(i>0&&((blocks.get(i-1).type==JsonBlock.TYPE_BLOCK_COMMA&&
								lineLength+jb.bracketEnd-jb.begin>JSON_LENGTH_PER_LINE)||blocks.get(i-1).type==JsonBlock.TYPE_BLOCK_START)){
							newLine(sb,jb.level);
							lineLength=0;
						}
					}
					lineLength+=jb.end-jb.begin;
					if(i+1<blocks.size()&&blocks.get(i+1).type==JsonBlock.TYPE_BLOCK_COLON){
						sb.append("<i class='k'>");
						buildJsonProp(sb,chars,jb.begin,jb.end);
						sb.append("</i>");
					}else{
						if(jb.type==JsonBlock.TYPE_BLOCK_CONTENT){
							String s=new String(chars,jb.begin,jb.end-jb.begin);
							if("true".equals(s)||"false".equals(s)||"null".equals(s)){
								sb.append("<i class='k'>");
								sb.append(s);
								sb.append("</i>");
							}else{
								sb.append(s);
							}
						}else{
							buildJsonProp(sb,chars,jb.begin,jb.end);
						}
					}
					break;
				}
			}
		}
		return blocks.size();
	}

	private static class JsonBlock{
		static final int TYPE_BLOCK_START=3;// 大括号或中括号开头{[
		static final int TYPE_BLOCK_END=4;// 大括号或中括号结尾}]
		static final int TYPE_BLOCK_COLON=5;//冒号:
		static final int TYPE_BLOCK_COMMA=6;//逗号,
		static final int TYPE_BLOCK_CONTENT=1;//内容项一般是null 数字 true false
		static final int TYPE_BLOCK_QUTA=2;//包含在括号内的内容项，文本。
		char[] chars;
		int type;
		int begin;
		int end;
		int bracketEnd;
		int level;
		boolean son;
		JsonBlock(int type,int begin,int end,int level,char[] chars){
			this.type=type;
			this.begin=begin;
			this.end=end;
			this.level=level;
			this.chars=chars;
		}
		public String toString(){
			StringBuilder sb=new StringBuilder();
			sb.append("{type=");
			switch(type){
				case TYPE_BLOCK_START:{
					sb.append("START");
					break;
				}
				case TYPE_BLOCK_END:{
					sb.append("END");
					break;
				}
				case TYPE_BLOCK_COLON:{
					sb.append("COLON");
					break;
				}
				case TYPE_BLOCK_COMMA:{
					sb.append("COMMA");
					break;
				}
				case TYPE_BLOCK_CONTENT:{
					sb.append("CONTENT");
					break;
				}
				case TYPE_BLOCK_QUTA:{
					sb.append("QUTA");
					break;
				}
			}
			sb.append(",level=");
			sb.append(level);
			sb.append(",begin=");
			sb.append(begin);
			sb.append(",end=");
			sb.append(end);
			sb.append(",bracketEnd=");
			sb.append(bracketEnd);
			sb.append(",chars=");
			sb.append(new String(chars,begin,end-begin));
			sb.append(')');
			return sb.toString();
		}
	}
	private static int visitJson(List<JsonBlock> blocks,JsonBlock parent,char[] c, int begin,int level) {
		//先快速扫描关键标签，但不进行构建
		JsonBlock keyBlock=null;
		JsonBlock valueBlock=null;
		for(int i=begin;i<c.length;i++){
			if(c[i]=='['||c[i]=='{'){
				JsonBlock jb = new JsonBlock(JsonBlock.TYPE_BLOCK_START,i,i+1,level,c);
				blocks.add(jb);
				i=visitJson(blocks,jb, c, i+1, level+1);
				if(parent!=null){
					parent.son=true;
				}
				jb.bracketEnd=i+1;
			}else if(c[i]=='}'||c[i]==']'){
				if(blocks.size()>0){
					JsonBlock lastBlock=blocks.get(blocks.size()-1);
					valueBlock=lastBlock;
					if(keyBlock!=null){
						keyBlock.bracketEnd=valueBlock.end;
					}
					valueBlock.bracketEnd=valueBlock.end;
					keyBlock=null;
				}
				blocks.add(new JsonBlock(JsonBlock.TYPE_BLOCK_END,i,i+1,level,c));
				return i;
			}else if(c[i]=='"'){
				i=visitComma(blocks, c, i, level);
			}else if(c[i]==':'){
				if(blocks.size()>0){
					JsonBlock lastBlock=blocks.get(blocks.size()-1);
					keyBlock=lastBlock;
				}
				blocks.add(new JsonBlock(JsonBlock.TYPE_BLOCK_COLON,i,i+1,level,c));
			}else if(c[i]==','){
				if(blocks.size()>0){
					JsonBlock lastBlock=blocks.get(blocks.size()-1);
					valueBlock=lastBlock;
					if(keyBlock!=null){
						keyBlock.bracketEnd=valueBlock.end;
					}
					valueBlock.bracketEnd=valueBlock.end;
					keyBlock=null;
				}
				blocks.add(new JsonBlock(JsonBlock.TYPE_BLOCK_COMMA,i,i+1,level,c));
			}else{
				boolean addToLastBlock=false;
				if(blocks.size()>0){
					JsonBlock lastBlock=blocks.get(blocks.size()-1);
					if(lastBlock.type==JsonBlock.TYPE_BLOCK_CONTENT){
						lastBlock.end=i+1;
						addToLastBlock=true;
					}
				}
				if(!addToLastBlock){
					blocks.add(new JsonBlock(JsonBlock.TYPE_BLOCK_CONTENT,i,i+1,level,c));
				}
			}
		}
		//如果有下级需要启动换行模式
		return c.length;
	}
	
	private static void newLine(StringBuilder sb, int level) {
		sb.append("<br/>");
		for(int i=0;i<level;i++){
			sb.append("&nbsp;&nbsp;");
		}
	}
	private static void buildJsonProp(StringBuilder sb, char[] c, int begin, int end) {
		for(int i=begin;i<end;i++){
			if(c[i]=='&'){
    			sb.append("&amp;");
    		}else if(c[i]=='<'){
    			sb.append("&lt;");
    		}else if(c[i]=='>'){
    			sb.append("&gt;");
    		}else {
    			sb.append(c[i]);
    		}
		}
	}

	private static int visitComma(List<JsonBlock>blocks, char[] c, int begin, int level) {
		for(int i=begin+1;i<c.length;i++){
			if(c[i]=='\\'){
				i++;
			}else if(c[i]=='"'){
				blocks.add(new JsonBlock(JsonBlock.TYPE_BLOCK_QUTA,begin,i+1,level,c));
				return i;
			}
		}
		return c.length;
	}
	
}
